﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace CommonFramework.Log
{
    public static class LogUtil
    {
        static LogUtil()
        {
            SetHandlerActive<DefaultLogMessageHandler>();
        }

        #region 可输出日志级别开关

        public static bool IsLogSeverityEnabled(LogSeverity severity)
        {
            return (_logMessageMaker.LogServerityFlagMask & (uint)severity) > 0;
        }
        public static void SetLogSeverityEnabled(LogSeverity severity, bool enabled)
        {
            if (enabled)
                _logMessageMaker.LogServerityFlagMask |= (uint) severity;
            else
                _logMessageMaker.LogServerityFlagMask &= ~(uint)severity;
        }

        #endregion

        #region 产出日志消息

        private static LogMessageMaker _logMessageMaker = new LogMessageMaker();
        public static void LogError(string msg)
        {
            msg = $"{msg}\n(Stack Trace):\n{LogUtil.StackTrace(2)}";
            _logMessageMaker.LogError(msg);
        }
        /// <summary>
        /// 输出带异常的错误日志
        /// </summary>
        public static void LogError(string msg, Exception e)
        {
            if (e == null)
            {
                LogError(msg);
            }
            else
            {
                msg = $"{msg} with (Exception)={e.GetType().Name} {e.Message}";
                msg = $"{msg}\n(Stack Trace):\n{e.StackTrace}";
                _logMessageMaker.MakeLogMessage(LogSeverity.Error, msg, e);
            }
        }
        public static void LogErrorFormat(string msg, params object[] formatArgs)
            => _logMessageMaker.LogErrorFormat(msg, formatArgs);
        public static void LogNotReachHereError(string msg = "")
        {
            if (string.IsNullOrEmpty(msg))
                msg = "Not Reach Here!";
            msg = $"{msg}\nstack trace:\n{LogUtil.StackTrace(2)}";
            _logMessageMaker.LogError(msg);
        }

        public static void LogWarning(string msg)
        {
            msg = $"{msg}\nstack trace:\n{LogUtil.StackTrace(2)}";
            _logMessageMaker.LogWarning(msg);
        }

        public static void LogWarningFormat(string msg, params object[] formatArgs)
            => _logMessageMaker.LogWarningFormat(msg, formatArgs);

        public static void Log(string msg)
            => _logMessageMaker.Log(msg);

        public static void LogFormat(string msg, params object[] formatArgs)
            => _logMessageMaker.LogFormat(msg, formatArgs);

        public static void LogVerbose(string msg)
            => _logMessageMaker.LogVerbose(msg);

        public static void LogVerboseFormat(string msg, params object[] formatArgs)
            => _logMessageMaker.LogVerboseFormat(msg, formatArgs);

        #endregion

        #region 日志消息回调处理

        /// <summary>
        /// 添加或移除日志消息的产出回调,可以在回调中进行日志消息不同方式的存储,过滤和输出
        /// </summary>
        public static void ListenLogMessage(OnLogMessageDelegate onLogMessage,bool isAdd=true)
        {
            if(isAdd)
                _logMessageMaker.OnLogMessageCreated += onLogMessage;
            else
                _logMessageMaker.OnLogMessageCreated -= onLogMessage;
        }

        /// <summary>
        /// 当前正在启用的日志消息处理器
        /// </summary>
        private static Dictionary<Type, LogMessageHandler> _activedLogMessageHandlers =
            new Dictionary<Type, LogMessageHandler>();
        /// <summary>
        /// 设置指定类型的日志消息处理是否启用
        /// </summary>
        public static Handler_T SetHandlerActive<Handler_T>(bool isActive = true) 
            where Handler_T : LogMessageHandler, new()
        {
            Type typeKey = typeof(Handler_T);
            
            if (isActive && _activedLogMessageHandlers.ContainsKey(typeKey)==false)
            {
                var handler = new Handler_T();
                _activedLogMessageHandlers.Add(typeKey,handler);
                return handler;
            }

            if (!isActive && _activedLogMessageHandlers.ContainsKey(typeKey))
            {
                var handler= _activedLogMessageHandlers[typeKey];
                _activedLogMessageHandlers.Remove(typeKey);

                handler.Dispose();
                return null;
            }

            if (_activedLogMessageHandlers.TryGetValue(typeKey, out var activedHandler))
                return activedHandler as Handler_T;
            else
                return null;
        }
        /// <summary>
        /// 获取当前使用的日志消息处理器
        /// </summary>
        public static List<LogMessageHandler> GetAllActivedHandlers()
        {
            return _activedLogMessageHandlers.Values.ToList();
        }
        /// <summary>
        /// 指定的日志消息处理器是否在使用?
        /// </summary>
        public static bool GetHandlerActive<T>() where T : LogMessageHandler
        {
            return _activedLogMessageHandlers.ContainsKey(typeof(T));
        }
        
        #endregion

        #region 日志消息部件

        /// <summary>
        /// 给日志附加时间头,如 "[2024-4-11 20:59:59]"
        /// </summary>
        public static string TimeHead(string wrapLeft = "[", string wrapRight = "]")
        {
            return $"{wrapLeft}{DateTime.Now.ToString("yyyy-M-d HH:mm:ss")}{wrapRight}";
        }

        private static StringBuilder _stackTraceStr = new StringBuilder();
        /// <summary>
        /// 给日志加上栈帧内容,如 "Applicatioin.cs Applicatioin.Main():11"
        /// </summary>
        public static string StackTrace(int skipFrames = 1)
        {
            StackTrace stackTrace = new StackTrace(fNeedFileInfo: true);
            StackFrame[] frames = stackTrace.GetFrames();
            if (frames == null || frames.Length == 0)
                return "";

            lock (_stackTraceStr)
            {
                _stackTraceStr.Clear();
                for (int i = skipFrames; i < frames.Length; ++i)
                {
                    StackFrame frame = frames[i];
                    string frameStr = "";
                    if (string.IsNullOrEmpty(frame.GetFileName()))
                    {
                        frameStr = string.Format("{0}.{1}()",
                            frame.GetMethod().DeclaringType?.Name,
                            frame.GetMethod().Name);
                    }
                    else
                    {
                        frameStr = string.Format("{0}:{3} {1}.{2}()",
                            Path.GetFileName(frame.GetFileName()),
                            frame.GetMethod().DeclaringType?.Name,
                            frame.GetMethod().Name,
                            frame.GetFileLineNumber());
                    }
                    _stackTraceStr.AppendLine(frameStr);
                }
                return _stackTraceStr.ToString();
            }
        }

        #endregion
    }
}